package com.psytech.app.task.web_ui.server;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.Context;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;

import taskmanager.psytech.com.taskmanagerservice._1.Action;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.ibm.icu.text.SimpleDateFormat;
import com.psytech.app.task.driver.user.impl.xml.XmlUserDriver;
import com.psytech.app.task.web_ui.Comment;
import com.psytech.app.task.web_ui.Group;
import com.psytech.app.task.web_ui.Task;
import com.psytech.app.task.web_ui.User;
import com.psytech.app.task.web_ui.client.TaskManagerService;
import com.psytech.app.task.web_ui.driver.TaskDriverInterface;
import com.psytech.app.task.web_ui.driver.exception.InvalidCriteria;
import com.psytech.app.task.web_ui.driver.exception.ObjectNotFoundException;
import com.psytech.app.task.web_ui.shared.UIComment;
import com.psytech.app.task.web_ui.shared.UIGroup;
import com.psytech.app.task.web_ui.shared.UIUser;
import com.psytech.app.task.web_ui.shared.UiTask;
import com.psytech.app.task.web_ui.shared.UiTask.PRIORITY;
import com.psytech.app.task.web_ui.shared.UiTask.STATUS;

public class TaskManagerServiceImpl extends RemoteServiceServlet implements TaskManagerService {

	private static Logger LOG = Logger.getLogger(TaskManagerServiceImpl.class.getName());

	private static final long serialVersionUID = 2800698580333960128L;

	private final TaskDriverInterface task_driver = (TaskDriverInterface) Initializer.springContext
			.getBean("taskDriver");

	private final XmlUserDriver user_driver = (XmlUserDriver) Initializer.springContext.getBean("userDriver");

	private final String smtpHostname = (String) Initializer.springContext.getBean("smtpHost");

	private final String defaultSender = (String) Initializer.springContext.getBean("smtpSender");

	private static Context env;

	@Override
	public List<UiTask> listAllTask(final UIUser uiUser) throws IllegalArgumentException, IllegalStateException {
		try {
			final HashSet<Task> tasksSet = new HashSet<Task>();
			final Task taskCriteria = new Task();
			taskCriteria.setCreatorUid(uiUser.getUid());
			tasksSet.addAll(this.task_driver.searchTask(taskCriteria));
			final com.psytech.app.task.web_ui.User user = new com.psytech.app.task.web_ui.User();
			user.setUid(uiUser.getUid());
			tasksSet.addAll(this.task_driver.searchTasksAssignedToUsers(user));
			final List<UiTask> uiTasks = new ArrayList<UiTask>();
			for (final Task task : tasksSet) {
				uiTasks.add(this.taskToUITask(task));
			}
			return uiTasks;
		} catch (final InvalidCriteria e) {
			LOG.log(Level.WARNING, "", e);
			throw new IllegalArgumentException("Certain critères ne sont pas accéptés pour une recherche", e);
		} catch (final ObjectNotFoundException e) {
			LOG.log(Level.WARNING, "", e);
			throw new IllegalArgumentException("Aucune tâche ne correspond à ces critères");
		}

	}

	@Override
	public List<UiTask> listAffectedTask(final UIUser uiUser) throws IllegalArgumentException, IllegalStateException {
		try {
			final com.psytech.app.task.web_ui.User user = new com.psytech.app.task.web_ui.User();
			user.setUid(uiUser.getUid());
			final List<Task> tasks = this.task_driver.searchTasksAssignedToUsers(user);
			final List<UiTask> uiTasks = new ArrayList<UiTask>();
			for (final Task task : tasks) {
				uiTasks.add(this.taskToUITask(task));
			}
			return uiTasks;
		} catch (final ObjectNotFoundException e) {
			LOG.log(Level.WARNING, "", e);
			throw new IllegalArgumentException("Aucune tâche ne correspond à ces critères");
		}

	}

	@Override
	public List<UiTask> listAllTeamTask() throws IllegalArgumentException, IllegalStateException {
		try {
			final Task taskCriteria = new Task();
			final List<Task> tasks = this.task_driver.searchTask(taskCriteria);
			final List<UiTask> uiTasks = new ArrayList<UiTask>();
			for (final Task task : tasks) {
				uiTasks.add(this.taskToUITask(task));
			}
			return uiTasks;
		} catch (final InvalidCriteria e) {
			LOG.log(Level.WARNING, "", e);
			throw new IllegalArgumentException("Certains critères ne sont pas accéptés pour une recherche", e);
		}
	}

	@Override
	public List<UiTask> listUnaffectedTask() throws IllegalArgumentException, IllegalStateException {
		try {
			final Task taskCriteria = new Task();
			final List<Task> tasks = this.task_driver.searchNotAssignedTasks(taskCriteria);
			final List<UiTask> uiTasks = new ArrayList<UiTask>();
			for (final Task task : tasks) {
				uiTasks.add(this.taskToUITask(task));
			}
			return uiTasks;
		} catch (final ObjectNotFoundException e) {
			LOG.log(Level.WARNING, "", e);
			throw new IllegalArgumentException("Aucune tâche ne correspond à ces critères");
		} catch (final InvalidCriteria e) {
			LOG.log(Level.WARNING, "", e);
			throw new IllegalArgumentException("Critères invalides");
		}
	}

	@Override
	public UiTask saveTask(final UiTask task) throws IllegalArgumentException, IllegalStateException {
		if (task.getUid() == null)
			return this.manageTask(task, Action.CREATE);
		else
			return this.manageTask(task, Action.UPDATE);
	}

	private UiTask manageTask(final UiTask uiTask, final Action action) {
		try {
			Task task = this.UiTaskToTask(uiTask);
			if (action.equals(Action.CREATE)) {
				task = this.task_driver.createTask(task);
			} else {
				Task existingTask = new Task();
				existingTask.setUid(task.getUid());
				final List<Task> existingTasks = this.task_driver.searchTask(existingTask);
				if (existingTasks.size() > 0) {
					existingTask = existingTasks.get(0);
					if (!ObjectUtils.equals(existingTask.getName(), task.getName())) {
						existingTask = this.task_driver.updateTaskName(task);
					}
					if (!ObjectUtils.equals(existingTask.getDescription(), task.getDescription())) {
						existingTask = this.task_driver.updateTaskDescription(task);
					}
					if (existingTask.getExpectedEndDate() == null && task.getExpectedEndDate() != null
							|| existingTask.getExpectedEndDate() != null && task.getExpectedEndDate() == null
							|| existingTask.getExpectedEndDate() != null && task.getExpectedEndDate() != null
							&& existingTask.getExpectedEndDate().compareTo(task.getExpectedEndDate()) != 0) {
						final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
						LOG.log(Level.FINE, "ExpectedEndDate : " + sdf.format(task.getExpectedEndDate()));
						existingTask = this.task_driver.updateExpectedEndDate(task);
					}
					if (existingTask.getExpectedStartDate() == null && task.getExpectedStartDate() != null
							|| existingTask.getExpectedStartDate() != null && task.getExpectedStartDate() == null
							|| existingTask.getExpectedStartDate() != null && task.getExpectedStartDate() != null
							&& existingTask.getExpectedStartDate().compareTo(task.getExpectedStartDate()) != 0) {
						final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
						LOG.log(Level.FINE, "ExpectedStartDate : " + sdf.format(task.getExpectedStartDate()));
						existingTask = this.task_driver.updateExpectedStartDate(task);
					}
					if (!ObjectUtils.equals(existingTask.getPriority(), task.getPriority())) {
						if (task.getPriority() == com.psytech.app.task.web_ui.Task.PRIORITY.HIGH) {
							existingTask = this.task_driver.increasePriority(task);
						}
						if (task.getPriority() == com.psytech.app.task.web_ui.Task.PRIORITY.MEDIUM) {
							existingTask = this.task_driver.unSpecifiPriority(task);
						}
						if (task.getPriority() == com.psytech.app.task.web_ui.Task.PRIORITY.LOW) {
							existingTask = this.task_driver.lowPriority(task);
						}
						{
							// TODO déplacer
							final String subject = "TASKMAN\\ " + uiTask.getName() + " \\ Changement de priorité : "
									+ task.getPriority().toString();
							if (this.user_driver.getUserByUid(task.getCreatorUid()).getEmail() != null) {
								this.sendMail(this.user_driver.getUserByUid(task.getCreatorUid()).getEmail(), subject,
										null);
							}
							if (this.user_driver.getUserByUid(task.getAssignedUserUid()).getEmail() != null) {
								this.sendMail(this.user_driver.getUserByUid(task.getAssignedUserUid()).getEmail(),
										subject, null);
							}
						}

					}
					if (!ObjectUtils.equals(existingTask.getStatus(), task.getStatus())) {
						if (task.getStatus() == com.psytech.app.task.web_ui.Task.STATUS.PENDING) {
							existingTask = this.task_driver.suspendTask(task);
						}
						if (task.getStatus() == com.psytech.app.task.web_ui.Task.STATUS.PROGRESS) {
							existingTask = this.task_driver.beginTask(task);
						}
						if (task.getStatus() == com.psytech.app.task.web_ui.Task.STATUS.COMPLETE) {
							existingTask = this.task_driver.completeTask(task);
						}
						final String subject = "TASKMAN\\ " + uiTask.getName() + " \\ Changement de statut : "
								+ task.getStatus().toString();
						if (this.user_driver.getUserByUid(task.getCreatorUid()).getEmail() != null) {
							this.sendMail(this.user_driver.getUserByUid(task.getCreatorUid()).getEmail(), subject, null);
						}
						if (this.user_driver.getUserByUid(task.getAssignedUserUid()).getEmail() != null) {
							this.sendMail(this.user_driver.getUserByUid(task.getAssignedUserUid()).getEmail(), subject,
									null);
						}

					}
					if (!ObjectUtils.equals(existingTask.getAssignedUserUid(), task.getAssignedUserUid())) {
						existingTask = this.task_driver.assignsTaskToUser(task);
						{
							final String subject_assign = "TASKMAN\\ " + task.getName() + " \\ Tâche affectée";
							if (this.user_driver.getUserByUid(task.getCreatorUid()).getEmail() != null) {
								this.sendMail(this.user_driver.getUserByUid(task.getCreatorUid()).getEmail(),
										subject_assign, null);
							}
						}
						{
							final String subject_unassign = "TASKMAN\\ " + task.getName() + " \\ Tâche désaffectée";
							if (this.user_driver.getUserByUid(task.getAssignedUserUid()).getEmail() != null) {
								this.sendMail(this.user_driver.getUserByUid(task.getAssignedUserUid()).getEmail(),
										subject_unassign, null);
							}
						}
					}

				}
			}
			return this.taskToUITask(task);
		} catch (final InvalidCriteria e) {
			LOG.log(Level.WARNING, "", e);
			throw new IllegalArgumentException("Certains critères ne sont pas accéptés pour une recherche", e);
		} catch (final ObjectNotFoundException e) {
			LOG.log(Level.WARNING, "", e);
			throw new IllegalArgumentException("Aucune tâche ne correspond à ces critères");
		}

	}

	private UiTask taskToUITask(final Task task) {
		final UiTask tmpTask = new UiTask();
		tmpTask.setUid(task.getUid());
		tmpTask.setName(task.getName());
		tmpTask.setDescription(task.getDescription());
		tmpTask.setPriority(PRIORITY.valueOf(task.getPriority().toString()));
		tmpTask.setStatus(STATUS.valueOf(task.getStatus().toString()));
		if (task.getExpectedEndDate() != null) {
			tmpTask.setExpectedEndDate(task.getExpectedEndDate());
		}
		if (task.getExpectedStartDate() != null) {
			tmpTask.setExpectedStartDate(task.getExpectedStartDate());
		}
		try {
			tmpTask.setCreator(this.userToUIUser(this.user_driver.getUserByUid(task.getCreatorUid())));
		} catch (final ObjectNotFoundException e) {
			LOG.log(Level.WARNING, "Creator not found", e);
			throw new IllegalArgumentException("Creator not found");
		}
		if (task.getAssignedUserUid() != null) {
			try {
				tmpTask.getAffectedUsers().add(
						this.userToUIUser(this.user_driver.getUserByUid(task.getAssignedUserUid())));
			} catch (final ObjectNotFoundException e) {
				LOG.log(Level.WARNING, "User not found", e);
				throw new IllegalArgumentException("User not found");
			}
		}
		return tmpTask;
	}

	@Override
	public void removeTask(final String taskUid) throws IllegalArgumentException, IllegalStateException {
		final Task taskToRemove = this.getLightTask(taskUid);
		try {
			this.task_driver.deleteTask(taskToRemove);
		} catch (final ObjectNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public UiTask searchTaskByUID(final String uid) throws IllegalArgumentException, IllegalStateException {
		final Task task = new Task();
		task.setUid(uid);
		List<Task> tasks;
		try {
			tasks = this.task_driver.searchTask(task);
			if (tasks.size() == 1)
				return this.taskToUITask(tasks.get(0));
			else
				return new UiTask();
		} catch (final InvalidCriteria e) {
			// TODO
			throw new IllegalArgumentException(e);
		}

	}

	@Override
	public void createComment(final UIComment uiComment) throws IllegalArgumentException, IllegalStateException {

		final Comment comment = new Comment();
		comment.setTaskUid(uiComment.getTaskUid());
		comment.setText(uiComment.getText());
		comment.setWriterUid(uiComment.getWriter().getUid());

		try {
			this.task_driver.createComment(comment);

			final Task task = new Task();
			task.setUid(comment.getTaskUid());
			final List<Task> tasks = this.task_driver.searchTask(task);

			// TODO enlever l'envoie de mail et le déplacer dans du code
			// spécifique
			final String subject = "TASKMAN\\ " + tasks.get(0).getName() + "\\ Commentaire ajouté";
			if (comment.getWriterUid().equals(tasks.get(0).getCreatorUid())) {
				if (this.user_driver.getUserByUid(tasks.get(0).getAssignedUserUid()).getEmail() != null) {
					this.sendMail(this.user_driver.getUserByUid(tasks.get(0).getAssignedUserUid()).getEmail(), subject,
							null);
				}
			} else {
				if (this.user_driver.getUserByUid(task.getCreatorUid()).getEmail() != null) {
					this.sendMail(this.user_driver.getUserByUid(task.getCreatorUid()).getEmail(), subject, null);
				}
			}

		} catch (final ObjectNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (final InvalidCriteria e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public void deleteComment(final String commentUid) throws IllegalArgumentException, IllegalStateException {

		final com.psytech.app.task.web_ui.Comment comment = new com.psytech.app.task.web_ui.Comment();
		comment.setUid(commentUid);
		try {
			this.task_driver.deleteComment(comment);
		} catch (final ObjectNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public List<UIComment> getComments(final String taskUid) throws IllegalArgumentException, IllegalStateException {

		final Task task = new Task();
		task.setUid(taskUid);
		final List<UIComment> uiComments = new ArrayList<UIComment>();
		try {
			for (final Comment comment : this.task_driver.getCommentsByTask(task)) {
				uiComments.add(this.commentToUIComment(comment));
			}
		} catch (final ObjectNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return uiComments;
	}

	private UIComment commentToUIComment(final Comment comment) {

		final UIComment uiComment = new UIComment();
		uiComment.setUid(comment.getUid());
		uiComment.setCreationDate(comment.getCreationDate());
		uiComment.setTaskUid(comment.getTaskUid());
		uiComment.setText(comment.getText());
		UIUser user;
		try {
			user = this.userToUIUser(this.user_driver.getUserByUid(comment.getWriterUid()));
			uiComment.setWriter(user);
		} catch (final ObjectNotFoundException e) {
			LOG.log(Level.WARNING, "Unknow writer");
			throw new IllegalStateException("Unknow writer");
		}
		return uiComment;
	}

	private UIUser userToUIUser(final User user) {
		assert user != null;
		final UIUser uiuser = new UIUser();
		uiuser.setLogin(user.getLogin());
		uiuser.setUid(user.getUid());
		return uiuser;
	}

	private Task getLightTask(final String uid) {
		assert uid != null;
		final Task task = new Task();
		task.setUid(uid);
		return task;
	}

	private UIGroup groupToUiGroup(final Group group) {
		final UIGroup uigroup = new UIGroup();
		uigroup.setGuid(group.getGuid());
		uigroup.setName(group.getName());
		return uigroup;
	}

	private Task UiTaskToTask(final UiTask uiTask) {
		final Task task = this.getLightTask(uiTask.getUid());
		task.setDescription(uiTask.getDescription());
		task.setName(uiTask.getName());
		task.setExpectedEndDate(uiTask.getExpectedEndDate());
		task.setExpectedStartDate(uiTask.getExpectedStartDate());
		if (uiTask.getAffectedUsers().size() > 0) {
			task.setAssignedUserUid(uiTask.getAffectedUsers().get(0).getUid());
		}
		task.setPriority(com.psytech.app.task.web_ui.Task.PRIORITY.valueOf(uiTask.getPriority().toString()));
		task.setStatus(com.psytech.app.task.web_ui.Task.STATUS.valueOf(uiTask.getStatus().toString()));
		task.setCreatorUid(uiTask.getCreator().getUid());
		return task;
	}

	@Override
	public List<UIUser> getTeamMember(final String groupUid) throws IllegalArgumentException, IllegalStateException {
		final List<UIUser> uisers = new ArrayList<UIUser>();
		try {
			for (final User user : this.user_driver.getGroupMembers(groupUid)) {
				uisers.add(this.userToUIUser(user));
			}
			return uisers;
		} catch (final ObjectNotFoundException e) {
			LOG.log(Level.WARNING, "Unknow group");
			throw new IllegalStateException("Groupe non reconnu !");
		}
		// return user_driver.getGroupMembers(groupUid);
	}

	@Override
	public UIUser whoAmI() throws IllegalArgumentException, IllegalStateException {
		LOG.log(Level.INFO, "whoAmI");
		if (this.user_driver.getUserByIp(this.getThreadLocalRequest().getRemoteAddr()) != null)
			return this.userToUIUser(this.user_driver.getUserByIp(this.getThreadLocalRequest().getRemoteAddr()));
		else {
			LOG.log(Level.WARNING, "Unknow user");
			throw new IllegalStateException("Utilisateur non reconnu !");
		}
	}

	@Override
	public List<UIGroup> getGroups(final String userUid) throws IllegalArgumentException, IllegalStateException {
		final List<UIGroup> groups = new ArrayList<UIGroup>();
		try {
			for (final Group group : this.user_driver.getUserGroups(userUid)) {
				groups.add(this.groupToUiGroup(group));
			}
		} catch (final ObjectNotFoundException e) {
			throw new IllegalArgumentException("Le groupe spécifié n'est pas trouvé");

		}
		return groups;
	}

	private void sendMail(final String recipient, final String subject, StringBuilder content) {
		assert recipient != null;
		assert subject != null;
		if (content == null) {
			content = new StringBuilder();
		}
		LOG.log(Level.FINE, "Envoie de mail");
		try {
			final HtmlEmail email = new HtmlEmail();
			email.setHostName(this.smtpHostname);
			email.addTo(recipient);
			email.setFrom(this.defaultSender);

			email.setSubject(subject.toString());

			content.insert(0, "<html>");
			content.append("</br><p style='color:grey;font-size:small'>This is an automatic mail sent by <a href='");
			content.append(this.getBaseUrl()).append("'>Taskman</a></p>");
			content.append("</html>");
			email.setHtmlMsg(content.toString());

			email.send();
			LOG.log(Level.FINE, "Mail envoyé à " + recipient);
			LOG.log(Level.FINE, subject.toString());
			LOG.log(Level.FINE, content.toString());
		} catch (final EmailException e) {
			LOG.warning("Erreur lors de l'envoie du mail " + e.getMessage());
		}
	}

	private String getBaseUrl() {
		String requestUrl = this.getThreadLocalRequest().getRequestURL().toString();
		requestUrl = requestUrl.substring(0, requestUrl.lastIndexOf("/"));
		return requestUrl.substring(0, requestUrl.lastIndexOf("/") + 1);
	}
}
